#pragma once
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <functional>
#include "buffer.hpp"

namespace mylog{
    class AsyncLooper{
    public:
    using ptr = std::shared_ptr<AsyncLooper>;
    using Functor = std::function<void(Buffer &buf)>;
    private:
        Functor _cb;
        Buffer _push_buf;
        Buffer _pop_buf;
        std::mutex _mtx;
        std::condition_variable _push_cond;
        std::condition_variable _pop_cond;
        std::thread _thread;
        std::atomic<bool> _running;
    private:
        void WorkerLoop(){
            while(1){
                {
                    std::unique_lock<std::mutex> lock(_mtx);
                    if(_running == false && _pop_buf.Empty()) return;
                    _pop_cond.wait(lock, [&]{ return _running == false || _push_buf.Empty() == false;});
                    _pop_buf.Swap(_push_buf);
                }
                _push_cond.notify_all();
                _cb(_pop_buf);
                _pop_buf.ReSet();
            }
            return;
        }
    public:
        AsyncLooper(const Functor& cb): _cb(cb), _running(true), _thread(&AsyncLooper::WorkerLoop, this){}
        ~AsyncLooper(){
            Stop();
        }
        void Stop(){
            _running = false;
            _pop_cond.notify_all();
            _thread.join();
        }
        void Push(const std::string& msg){
            if(_running == false) return;
            {
                std::unique_lock<std::mutex> lock(_mtx);
                _push_cond.wait(lock, [&]{ return _push_buf.WriteAbleSize() >= msg.size();});
                _push_buf.Push(msg.c_str(), msg.size());
            }
            _pop_cond.notify_all();
        }
    };
}